/******************************************************************************************************
 * Author		:	Chester Ragel
 * Date			:	01 - March - 2007
 * Description	:	Base class for context menu extenstion. This can be driven from any class to create context menus
 *					very easily	
 ******************************************************************************************************/

/*
 *  Context Menu Extention is the legal property of its developer
 *  Chester Ragel <chesterr@gmail.com> - Developer and Maintainer
 *	Sourceforge ID : chesterr
 *	Yahoo ID : chesterr1979 
 *	MSN : chesterr79@hotmail.com
 *	Gmail: chesterr@gmail.com
 *
 * If you want more information, found any bugs or have any doubts you 
 * can mail me or contact me through my yahoo id.
 * 
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 */

#region Namespace
using System;
using System.Text;
using System.Drawing;
using System.Collections;
using System.Windows.Forms;
using System.Runtime.InteropServices;
#endregion

namespace Keyrox.Shell {
    /// <summary>
    /// Base class for context menu extenstion. This can be driven from any class to create context menus
    /// very easily
    /// </summary>
    public abstract class BaseContextMenu : Win32.IContextMenu, Win32.IShellExtInit {

        #region Private Variables
        private uint drop;
        private Win32.IDataObject dataObject;
        private Hashtable hsIDCommand = new Hashtable();
        private MenuItems menus = new MenuItems();
        private int currentPosition = 0;
        private int basePosition = 0;
        private IntPtr roolIntPtr;
        private bool allowMultiSelect;
        #endregion

        #region Private Methods

        private void GetParentMenu(MenuItems menuContainer, MenuItems parentMenu, ref MenuItems foundMenu) {
            foreach (MenuItems mItems in menuContainer) {
                if (mItems == parentMenu) {
                    foundMenu = mItems;
                }
                else if (mItems != null && mItems.Count > 0) {
                    GetParentMenu(mItems, parentMenu, ref foundMenu);
                }
            }
        }

        private void BuildMenus(Win32.HMenu parentMenu, MenuItems menuItems) {
            foreach (MenuItems mItems in menuItems) {
                currentPosition++;
                if (mItems.Count > 0) {
                    Win32.HMenu mMainContainer = Win32.CreatePopupMenu();
                    mItems.Container = mMainContainer;
                    BuildMenus(mMainContainer, mItems);
                    Win32.InsertMenu(parentMenu, mItems.Location, Win32.MenuFlags.MF_BYPOSITION | Win32.MenuFlags.MF_POPUP, mMainContainer.handle, mItems.Text);
                    if (mItems.Icon != null) {
                        Bitmap bmp = mItems.Icon;
                        if (mItems.IsIconTransparent) {
                            Color backColor = bmp.GetPixel(1, 1);
                            bmp.MakeTransparent(backColor);
                        }
                        Win32.SetMenuItemBitmaps(parentMenu, mItems.Location, Win32.MenuFlags.MF_BYPOSITION, bmp.GetHbitmap(), bmp.GetHbitmap());
                    }
                }
                else {
                    if (roolIntPtr == parentMenu.handle) {
                        Win32.HMenu mMainContainer = Win32.CreatePopupMenu();
                        Win32.InsertMenu(parentMenu, mItems.Location, Win32.MenuFlags.MF_BYPOSITION, mMainContainer.handle, mItems.Text);
                        mItems.Container = mMainContainer;
                        if (mItems.Icon != null) {
                            Bitmap bmp = mItems.Icon;
                            if (mItems.IsIconTransparent) {
                                Color backColor = bmp.GetPixel(1, 1);
                                bmp.MakeTransparent(backColor);
                            }
                            Win32.SetMenuItemBitmaps(mItems.Parent.Container, mItems.Location, Win32.MenuFlags.MF_BYPOSITION, bmp.GetHbitmap(), bmp.GetHbitmap());
                        }
                    }
                    else if (mItems.IsSeperator) {
                        Win32.AppendMenu(parentMenu, Win32.MenuFlags.MF_SEPARATOR, new IntPtr(mItems.Location), mItems.Text);
                    }
                    else {
                        mItems.Container = parentMenu;
                        Win32.AppendMenu(parentMenu, Win32.MenuFlags.MF_STRING, new IntPtr(mItems.Location + basePosition), mItems.Text);
                        hsIDCommand.Add(mItems.Location, mItems);
                        if (mItems.Icon != null) {
                            Bitmap bmp = mItems.Icon;
                            if (mItems.IsIconTransparent) {
                                Color backColor = bmp.GetPixel(1, 1);
                                bmp.MakeTransparent(backColor);
                            }
                            Win32.SetMenuItemBitmaps(parentMenu, mItems.Location + basePosition, Win32.MenuFlags.MF_STRING, bmp.GetHbitmap(), bmp.GetHbitmap());
                        }
                    }
                }
            }
        }

        #endregion

        #region Public Properties

        /// <summary>
        /// Allow the selection of multiple files/folders etc. If false, the context menu wont be displayed
        /// while selecting multiple files
        /// </summary>
        public bool AllowMultiSelect {
            get {
                return allowMultiSelect;
            }
            set {
                allowMultiSelect = value;
            }
        }

        /// <summary>
        /// Gets or sets the name of the file.
        /// </summary>
        /// <value>The name of the file.</value>
        public string FileName { get; set; }
        #endregion

        #region Public Methods

        /// <summary>
        /// This is the abstract method which should be implemented by the driven class.
        /// </summary>
        public abstract void AssembleMenu();

        /// <summary>
        /// Add context menus into the parent containers
        /// </summary>
        /// <param name="parentMenu">Parent menu</param>
        /// <param name="childMenu">Child menu</param>
        /// <returns>true if added</returns>
        public bool AddMenu(MenuItems parentMenu, MenuItems childMenu) {
            bool bResult = false;
            try {
                MenuItems parent = null;
                GetParentMenu(menus, parentMenu, ref parent);
                if (parent == null) {
                    MessageBox.Show("MenuItem", "Parent Menu not found", MessageBoxButtons.OK, MessageBoxIcon.Error);
                }
                else {
                    childMenu.Parent = parent;
                    parent.Add(childMenu);
                    bResult = true;
                }
            }
            catch (Exception ex) {
                MessageBox.Show("MenuItem", ex.Message, MessageBoxButtons.OK, MessageBoxIcon.Error);
            }
            return bResult;
        }

        /// <summary>
        /// Insert the menu to the root context menu which the shell has. This will be called before AddMenu
        /// </summary>
        /// <param name="menus"></param>
        /// <returns></returns>
        public bool InsertMenu(MenuItems menus) {
            bool bResult = false;
            this.menus.Add(menus);
            menus.Parent = this.menus;
            return bResult;
        }

        #endregion

        #region Constructor

        /// <summary>
        /// Constructor
        /// </summary>
        public BaseContextMenu() {
            drop = 0;
            AssembleMenu();
        }

        #endregion

        #region IContextMenu Members

        int Win32.IContextMenu.QueryContextMenu(Win32.HMenu hMenu, uint indexMenu, uint idCmdFirst, int idCmdLast, Win32.ContextMenuFlag uFlags) {
            basePosition = (int)idCmdFirst;

            if (((uFlags & (Win32.ContextMenuFlag.CMF_NOVERBS | Win32.ContextMenuFlag.CMF_DEFAULTONLY | Win32.ContextMenuFlag.CMF_VERBSONLY)) == 0) || ((uFlags & (Win32.ContextMenuFlag.CMF_NORMAL | Win32.ContextMenuFlag.CMF_EXPLORE)) != 0)) {
                StringBuilder sbFile = new StringBuilder(1024);
                uint nSelected = Win32.DragQueryFile((IntPtr)drop, 0xffffffff, null, 0);
                roolIntPtr = hMenu.handle;
                if ((!AllowMultiSelect && nSelected == 1) || (nSelected > 1 && AllowMultiSelect)) {

                    StringBuilder sb = new StringBuilder(1024);
                    Win32.DragQueryFile((IntPtr)drop, 0, sb, (uint)(sb.Capacity + 1));
                    FileName = sb.ToString();
                                        
                    BuildMenus(hMenu, menus);
                }
            }
            return currentPosition;

        }

        void Win32.IContextMenu.GetCommandString(uint idCmd, uint uFlags, IntPtr pwReserved, IntPtr pszName, uint cchMax) {
            switch ((Win32.GCSFlags)uFlags) {
                default:
                    break;
            }
        }

        void Win32.IContextMenu.InvokeCommand(ref Win32.CMINVOKECOMMANDINFO pici) {
            MenuItems items = hsIDCommand[pici.verb] as MenuItems;
            if (items != null) {
                items.DoAction(FileName);
            }
        }

        #endregion

        #region IShellExtInit Members

        int Win32.IShellExtInit.Initialize(IntPtr pidlFolder, IntPtr lpdobj, uint hkeyProgID) {
            try {
                this.dataObject = null;
                if (lpdobj != IntPtr.Zero) {
                    this.dataObject = Marshal.GetObjectForIUnknown(lpdobj) as Win32.IDataObject;
                    Win32.FORMATETC formatetc = new Win32.FORMATETC();
                    formatetc.cfFormat = Win32.CLIPFORMAT.CF_HDROP;
                    formatetc.ptd = 0;
                    formatetc.dwAspect = Win32.DVASPECT.DVASPECT_CONTENT;
                    formatetc.lindex = -1;
                    formatetc.tymed = Win32.TYMED.TYMED_HGLOBAL;
                    Win32.STGMEDIUM stgmedium = new Win32.STGMEDIUM();
                    this.dataObject.GetData(ref formatetc, ref stgmedium);
                    this.drop = stgmedium.hGlobal;
                }
            }
            catch (Exception) {
            }
            return 0;
        }

        #endregion

    }
}
